package fr.utbm.gi.vi51.project.agent;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Stack;

import javax.swing.JOptionPane;
import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;

import org.janusproject.kernel.agent.Kernels;
import org.janusproject.kernel.status.Status;

import fr.utbm.gi.vi51.framework.FrameworkLauncher;
import fr.utbm.gi.vi51.framework.environment.Environment;
import fr.utbm.gi.vi51.framework.environment.Perception;
import fr.utbm.gi.vi51.framework.util.GeometryUtil;
import fr.utbm.gi.vi51.project.GlobalSettings;
import fr.utbm.gi.vi51.project.agent.body.EnemyBody;
import fr.utbm.gi.vi51.project.agent.body.EnemyMarineBody;
import fr.utbm.gi.vi51.project.agent.body.LeaderBody;
import fr.utbm.gi.vi51.project.agent.body.MutaliskBody;
import fr.utbm.gi.vi51.project.agent.body.UnitBody;
import fr.utbm.gi.vi51.project.agent.body.ZerglingBody;
import fr.utbm.gi.vi51.project.behavior.BehaviourOutput;
import fr.utbm.gi.vi51.project.environment.EnvironmentObject;
import fr.utbm.gi.vi51.project.environment.EnvironmentType;
import fr.utbm.gi.vi51.project.environment.WorldModel;
import fr.utbm.gi.vi51.project.formations.FormationBody;
import fr.utbm.gi.vi51.project.formations.FormationOrientation;
import fr.utbm.gi.vi51.project.formations.LineFormation;
import fr.utbm.gi.vi51.project.formations.SpreadFormation;
import fr.utbm.gi.vi51.project.formations.SquareFormation;

public class Leader extends Marine {

	private static final long serialVersionUID = -8105232065097334938L;
	private final static int MAX_FORMATION_COOLDOWN = 260;
	private final static int MAX_MOVE_COOLDOWN = 120;

	// Cooldowns
	private int attackCooldown = 0;
	private int formationCooldown = 0;
	private int moveCooldown = 0;
	private int wayPointCooldown = 0;

	// WayPoints
	private Stack<Point2d> wayPoints;

	public Leader(FormationBody formationBody, int formationPosition, Collection<Point2d> wayPoints) {
		super(formationBody, formationPosition);
		this.wayPoints = new Stack<>();
		if (wayPoints != null) {
			for (Point2d point : wayPoints) {
				this.wayPoints.push(point);
			}
		}
	}

	@Override
	protected LeaderBody createBody(Environment in) {
		return new LeaderBody(getAddress());
	}

	@Override
	public Status live() {
		// Check if leader is dead
		if (((UnitBody) getBody()).getHealth() <= 0) {
			this.killMe();
			Kernels.get().pause();
			JOptionPane.showConfirmDialog(null, "Leader is dead", "Game Over", JOptionPane.DEFAULT_OPTION); //$NON-NLS-1$ //$NON-NLS-2$
		} else {
			BehaviourOutput behaviour = null;
			List<Perception> perceivedObjects = getPerceivedObjects();
			// List of perceived obstacles and enemies
			int enemyMarineCount = 0, zerglingCount = 0, mutaliskCount = 0;
			List<Point2d> perceivedObstaclesPositions = new ArrayList<>();
			List<UnitBody> perceivedEnemies = new ArrayList<>();
			for (Perception perception : perceivedObjects) {
				if (perception.getPerceivedObject() instanceof EnvironmentObject) {
					// add perceived obstacles
					if (((EnvironmentObject) perception.getPerceivedObject()).getType() != EnvironmentType.TARGET) {
						perceivedObstaclesPositions.add(perception.getPerceivedObject().getPosition());
					} else {
						// go to target if present
						behaviour = this.seekBehaviour.runSeek(getPosition(), getDirection(), getCurrentLinearSpeed(),
								getMaxLinearSpeed(), new Point2d(perception.getPerceivedObject().getPosition().getX(),
										perception.getPerceivedObject().getPosition().getY()),
								perceivedObstaclesPositions);
						// Set new target is wayPoints isn't empty
						if (!this.wayPoints.isEmpty()) {
							double distanceToTarget = new Vector2d(perception.getPerceivedObject().getX()
									- getPosition().getX(), perception.getPerceivedObject().getY()
									- getPosition().getY()).length();
							if (distanceToTarget < 5) {
								// set new target
								Point2d newTarget = this.wayPoints.pop();
								FrameworkLauncher.deleteEnvironmentObject(WorldModel.getTarget());
								WorldModel.setTarget(new EnvironmentObject(48, (int) newTarget.getX(), (int) newTarget
										.getY(), EnvironmentType.TARGET, 48, 48));
								FrameworkLauncher.launchEnvironmentObject(WorldModel.getTarget());
								// set orientation
								Vector2d newOrientation = new Vector2d(newTarget.getX() - getPosition().getX(),
										newTarget.getY() - getPosition().getY());
								((LeaderBody) this.getBody()).setDirection(newOrientation);
								this.wayPointCooldown = MAX_MOVE_COOLDOWN;
							}
						}
					}
				} else if (perception.getPerceivedObject() instanceof EnemyBody) {
					perceivedEnemies.add((UnitBody) perception.getPerceivedObject());
					if (perception.getPerceivedObject() instanceof EnemyMarineBody) {
						enemyMarineCount++;
					}
					if (perception.getPerceivedObject() instanceof ZerglingBody) {
						zerglingCount++;
					}
					if (perception.getPerceivedObject() instanceof MutaliskBody) {
						mutaliskCount++;
					}
				}

			}
			// if the agent see an enemy, he shoots the nearest one !
			if (perceivedEnemies.size() > 0 && this.attackCooldown == 0) {
				UnitBody nearestEnemy = super.getNearestEnemy(perceivedEnemies);
				if (getBody().getFrustrum().isAttackable(getPosition(), nearestEnemy.getPosition())) {
					attack(nearestEnemy);
				}
			}

			// return to normal icon
			if (this.attackCooldown > 0 && this.attackCooldown < getBody().getAttackAnimationEnd()) {
				getBody().setCurrentIcon(getBody().getNormalIcon());
			}

			// Set formation position and orientation
			Marine.formationBody.setGlobalOrientation((float) GeometryUtil.toOrientationAngle(getDirection()));
			Marine.formationBody.setGlobalPosition(getPosition());

			if (behaviour != null && this.moveCooldown <= 0 && this.wayPointCooldown == 0) {
				influenceKinematic(behaviour.getLinear(), behaviour.getAngular());
			}

			// Smart change formation according to sight
			if (GlobalSettings.smartFormation && perceivedEnemies.size() > 5 && this.formationCooldown == 0) {
				FormationBody newFormation;
				if (enemyMarineCount == 0 && zerglingCount == 0) {
					newFormation = new SpreadFormation(FormationOrientation.EAST);
				}
				if (enemyMarineCount > zerglingCount) {
					newFormation = new LineFormation(FormationOrientation.EAST);
					if (mutaliskCount >= enemyMarineCount / 4 + 1) {
						newFormation = new SpreadFormation(FormationOrientation.EAST);
					}
				} else {
					newFormation = new SquareFormation(FormationOrientation.EAST);
					if (mutaliskCount >= zerglingCount / 4 + 1) {
						newFormation = new SpreadFormation(FormationOrientation.EAST);
					}
				}
				changeFormation(newFormation);
			}

			// Decrement cooldowns
			if (this.attackCooldown > 0) {
				this.attackCooldown--;
			}
			if (this.moveCooldown > 0) {
				this.moveCooldown--;
			}
			if (this.formationCooldown > 0) {
				this.formationCooldown--;
			}
			if (this.wayPointCooldown > 0) {
				this.wayPointCooldown--;
			}
		}
		return null;
	}

	@Override
	protected void attack(UnitBody unitBody) {
		// if the armor's prey is greatest than the shooter damage then nothing happens
		// else the prey damage equal to the damage minus armor
		if (this.getBody().getDamage() > unitBody.getArmor()) {
			unitBody.setHealth(unitBody.getHealth() - this.getBody().getDamage() + unitBody.getArmor());
			getBody().setCurrentIcon(getBody().getAttackIcon());
			this.attackCooldown = getBody().getAttackCooldown();
		}
	}

	protected void changeFormation(FormationBody formation) {
		if (Marine.formationBody.getClass() != formation.getClass()) {
			formation.scale(WorldModel.getMarines().size());
			Marine.formationBody = formation;
			this.formationCooldown = MAX_FORMATION_COOLDOWN;
			this.moveCooldown = MAX_MOVE_COOLDOWN;
		}
	}

}
